package com.cskaoyan.aspect;

import com.cskaoyan.bean.Admin;
import com.cskaoyan.bean.Log;
import com.cskaoyan.bean.Order;
import com.cskaoyan.mapper.LogMapper;
import com.cskaoyan.mapper.OrderMapper;
import com.cskaoyan.utils.CommonUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

@Aspect
@Component
public class Log4BackAspect {

    @Autowired
    LogMapper logMapper;

    @Autowired
    OrderMapper orderMapper;

    /**
     * 切入点的配置
     * value属性：切入点表达式
     * 方法名作为id
     */
    @Pointcut("execution(* com.cskaoyan.controller.admin.AuthController.*log(..)) " +
            "|| execution(* com.cskaoyan.service.AdminService.*log(..))" +
            "|| execution(* com.cskaoyan.service.OrderService.delete(..))"
    )
    public void pointcut() {
    }

    //类似于InvocationHandler的invoke(proxy,method,args)
    //类似于MethodInterceptor的invoke(methodInvocation)
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        // 这一部分比认证要早，可以获取请求相关的信息

        // 获取Request对象、（Response对象）
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // ServletWebRequest servletWebRequest = new ServletWebRequest(request);
        // HttpServletResponse response = servletWebRequest.getResponse();

        // 根据拦截的URL判断是什么类型的操作
        String requestURI = request.getRequestURI();
        String method = requestURI.replace(request.getContextPath() + "/admin/", "");

        Admin argAdmin = null;
        Integer orderId = null;
        if ("admin/create".equals(method) || "admin/delete".equals(method) || "admin/update".equals(method)){
            Object[] args = joinPoint.getArgs();
            argAdmin = (Admin) args[0];
        }
        else if ("order/delete".equals(method)){
            Object[] args = joinPoint.getArgs();
            orderId = (Integer) args[0];
        }
        else if ("auth/logout".equals(method)){ // 注销的话，执行完方法主体对象就失效了，所以要在这里做记录
            Subject sub = SecurityUtils.getSubject();
            argAdmin = (Admin) sub.getPrincipals().getPrimaryPrincipal();

            String ip = CommonUtil.getClientIP(request);
            // 基本数据的封装
            Log log = new Log();
            log.setAdmin(argAdmin.getUsername());
            log.setIp(ip);
            log.setAddTime(new Date());
            log.setUpdateTime(new Date());
            log.setAction("退出");

            logMapper.insertSelective(log);
        }



        Object proceed = joinPoint.proceed();



        // 注销了，主体对象没了，直接返回
        if ("auth/logout".equals(method)){
            return proceed;
        }

        // 获取主体对象(不要放前面，第一次登录转换成Admin失败，空指针)
        Subject subject = SecurityUtils.getSubject();
        Admin admin = (Admin) subject.getPrincipals().getPrimaryPrincipal();

        // 通用的数据获取：当前操作的管理员用户名、客户端IP地址、客户端操作时间
        String ip = CommonUtil.getClientIP(request);
        String username = admin.getUsername();
        // 基本数据的封装
        Log log = new Log();
        log.setIp(ip);
        log.setAdmin(username);
        log.setAddTime(new Date());
        log.setUpdateTime(new Date());

        if("auth/login".equals(method)){
            // 登录失败 -> 认证失败，不会走这里，会直接抛异常；所以登录失败由处理异常方法记录
            log.setAction("登录");
            logMapper.insertSelective(log);
        }
        else if ("order/delete".equals(method)){

            // 根据id去查订单号orderSn
            Order orderSn = orderMapper.selectByPrimaryKey(orderId);

            log.setResult(orderSn.getOrderSn());
            log.setType(2);
            log.setAction("删除订单");
            logMapper.insertSelective(log);
        }
        else if ("admin/create".equals(method)){
            log.setResult(argAdmin.getUsername());
            log.setAction("添加管理员");
            logMapper.insertSelective(log);
        }
        else if ("admin/delete".equals(method)){
            log.setResult(argAdmin.getUsername());
            log.setAction("删除管理员");
            logMapper.insertSelective(log);
        }
        else if ("admin/update".equals(method)){
            log.setResult(argAdmin.getUsername());
            log.setAction("编辑管理员");
            logMapper.insertSelective(log);
        }

        return proceed;
    }



    // 形参来接收委托类方法抛出的异常 Exception或Throwable
    // 这里用来处理登录失败的情况
    @AfterThrowing(value = "pointcut()", throwing = "exception")
    public void afterThrowing(Exception exception) {

        // 获取Request对象、（Response对象）
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // ServletWebRequest servletWebRequest = new ServletWebRequest(request);
        // HttpServletResponse response = servletWebRequest.getResponse();

        // 通用的数据获取：当前操作的管理员用户名、客户端IP地址、客户端操作时间
        String ip = CommonUtil.getClientIP(request);
        // 数据的封装
        Log log = new Log();
        log.setIp(ip);
        log.setAddTime(new Date());
        log.setUpdateTime(new Date());
        log.setStatus(false);
        log.setType(0);
        log.setAction("登录");
        log.setAdmin("匿名用户");
        log.setResult("用户帐号或密码不正确");

        String requestURI = request.getRequestURI();
        String method = requestURI.replace(request.getContextPath() + "/admin/", "");
        if ("auth/login".equals(method)){
            logMapper.insertSelective(log);
        }
    }
}
